home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Skunkware 5
/
Skunkware 5.iso
/
src
/
X11
/
xconq
/
draw.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-05-09
|
17KB
|
552 lines
/* Copyright (c) 1987, 1988 Stanley T. Shebs, University of Utah. */
/* This program may be used, copied, modified, and redistributed freely */
/* for noncommercial purposes, so long as this notice remains intact. */
#pragma comment(exestr, "@(#) draw.c 12.1 95/05/09 ")
/* RCS $Header: draw.c,v 1.3 88/07/18 17:10:44 shebs Exp $ */
/* Geometry is somewhat tricky because our viewports are supposed */
/* to wrap around a cylinder transparently. The general idea is that */
/* if modulo opns map x coordinates onto a cylinder, adding and subtracting */
/* the diameter of the cylinder "un-modulos" things. If this doesn't make */
/* any sense to you, then be careful about fiddling with the code! */
/* If that wasn't bad enough, the hexes constitute an oblique coordinate */
/* where the axes form a 60 degree angle to each other. Fortunately, no */
/* trig is necessary - to convert to/from rectangular, add/subtract 1/2 of */
/* the y coordinate to x, and leave the y coordinate alone. */
/* The graphical code uses mostly text drawing functions, which are more */
/* likely to be efficient than is random bitblting. (The interface may */
/* implement the operations as random blitting, but that's OK.) */
#include "config.h"
#include "misc.h"
#include "period.h"
#include "side.h"
#include "unit.h"
#include "map.h"
extern bool populations; /* used to decide about running pop display */
char rowbuf[BUFSIZE]; /* buffer for terrain row drawing */
/* redraw() has moved to the display code in X11.c and curses.c */
/* Ensure that given location is visible. We also flush the input because */
/* any input relating to a different screen is probably worthless. */
put_on_screen(side, x, y)
Side *side;
int x, y;
{
/* Ugly hack to prevent extra boxes being drawn during init - don't ask!*/
if (x == 0 && y == 0) return;
if (active_display(side)) {
if (!in_middle(side, x, y)) {
side->vcx = wrap(x);
side->vcy = min(max(side->vh2-(1-(side->vh&1)), y),
(world.height-1)-side->vh2);
if (side->lastvcx >= 0) undraw_box(side);
show_map(side);
if (side->lastvcx >= 0) draw_box(side);
flush_output(side);
flush_input(side);
}
}
}
/* Decide whether given location is not too close to edge of screen. */
/* We do this because it's a pain to move units when half the adjacent */
/* places aren't even visible. This routine effectively places a lower */
/* limit of 5x5 for the map window. (I think) */
in_middle(side, x, y)
Side *side;
int x, y;
{
int vcx = side->vcx, vcy = side->vcy;
int vw2 = side->vw2, vh2 = side->vh2;
int top, bot;
/* handle the boundary condition near top and bottom */
bot = vcy - vh2 + 2;
if (bot == 2) bot = 1;
top = vcy + vh2 - 2;
if (top == world.height-3) top = world.height - 2;
if (!between(bot, y, top))
return FALSE;
x = unwrap(side, x + (y - vcy) / 2);
return between(vcx-vw2+2, x, vcx+vw2-3+(side->vw&1));
}
/* Transform map coordinates into screen coordinates, relative to the given */
/* side. Allow for cylindricalness and number of pixels in a hex. */
xform(side, x, y, sxp, syp)
Side *side;
int x, y, *sxp, *syp;
{
*sxp = ((side->hw * (x - (side->vcx - side->vw2))) +
(side->hw * (y - side->vcy)) / 2);
*syp = side->hch * ((side->vcy + side->vh2) - y);
}
/* Undo the wrapping effect, relative to viewport location. */
/* Note that both conditions cannot both be true at the same time, */
/* since viewport is smaller than map. */
unwrap(side, x)
Side *side;
int x;
{
int vcx = side->vcx, vw2 = side->vw2, vw34 = side->vw2 + (side->vw2 >> 1);
if (vcx - vw2 < 0 && x > vcx + vw34) x -= world.width;
if (vcx + vw2 > world.width-1 && x < vcx - vw34) x += world.width;
return x;
}
/* Un-transform screen coordinates (as supplied by mouse perhaps) into */
/* map coordinates. This doesn't actually account for the details of */
/* hexagonal boundaries, and actually discriminates box-shaped areas. */
deform(side, sx, sy, xp, yp)
Side *side;
int sx, sy, *xp, *yp;
{
int vcx = side->vcx, vcy = side->vcy, adjust;
*yp = (vcy + side->vh2) - (sy / side->hch);
adjust = (((*yp - vcy) & 1) ? ((side->hw/2) * (*yp >= vcy ? 1 : -1)) : 0);
*xp = wrap(((sx - adjust) / side->hw) - (*yp - vcy) / 2 +
(vcx - side->vw2));
}
/* Transform coordinates in the world display. Simpler, since no moving */
/* viewport nonsense. */
w_xform(side, x, y, sxp, syp)
Side *side;
int x, y, *sxp, *syp;
{
*sxp = side->mm * x + (side->mm * y) / 2;
*syp = side->mm * (world.height - 1 - y);
}
/* Redraw the map of the whole world. We use square blobs instead of icons, */
/* since individual "hexes" may be as little as 1x1 pixels in size, and */
/* there are lots of them. Algorithm uses run-length encoding to find and */
/* draw bars of constant color. Monochrome just draws units - no good */
/* choices for terrain display. */
show_world(side)
Side *side;
{
int x, y, color, barcolor, x1;
if (active_display(side) && world_display(side)) {
clear_window(side, side->world);
for (y = world.height-1; y >= 0; --y) {
x1 = 0;
barcolor = world_color(side, x1, y);
for (x = 0; x < world.width; ++x) {
color = world_color(side, x, y);
if (color != barcolor) {
draw_bar(side, x1, y, x - x1, barcolor);
x1 = x;
barcolor = color;
}
}
draw_bar(side, x1, y, world.width - x1, barcolor);
}
if (side->vcy >= 0) draw_box(side);
}
}
/* Compute the color representing a hex from the given side's point of view. */
world_color(side, x, y)
Side *side;
int x, y;
{
int view = side_view(side, x, y);
Side *side2;
if (side->monochrome) {
return ((view == UNSEEN || view == EMPTY) ? side->bgcolor :
side->fgcolor);
} else {
if (view == UNSEEN) {
return (side->bgcolor);
} else if (view == EMPTY) {
return (side->hexcolor[terrain_at(x, y)]);
} else {
side2 = side_n(vside(view));
return ((side2 == NULL) ? side->neutcolor :
(allied_side(side2, side) ? side->altcolor :
side->enemycolor));
}
}
}
/* Draw an outline box on the world map. Since we adopt the dubious trick */
/* of inverting through all planes, must be careful to undo before moving; */
/* also, draw/undraw shiftedly so both boxes appear on both sides of world. */
draw_box(side)
Side *side;
{
invert_box(side, side->vcx, side->vcy);
invert_box(side, side->vcx - world.width, side->vcy);
side->lastvcx = side->vcx; side->lastvcy = side->vcy;
}
undraw_box(side)
Side *side;
{
invert_box(side, side->lastvcx, side->lastvcy);
invert_box(side, side->lastvcx - world.width, side->lastvcy);
}
/* Draw immediate area in more detail. We make a little effort to avoid */
/* drawing hexes off the visible part of the screen, but are still somewhat */
/* conservative, so as not to get holes in the display. Implication is that */
/* some lower level of routines has to be able to clip the map window. */
show_map(side)
Side *side;
{
int y1, y2, y, x1, x2, adj;
if (active_display(side)) {
clear_window(side, side->map);
y1 = side->vcy + side->vh2;
y2 = side->vcy - side->vh2 + 1 - (side->vh & 1);
for (y = y1; y >= y2; --y) {
adj = (y - side->vcy) / 2;
x1 = side->vcx - side->vw2 - adj - 1;
x2 = side->vcx + side->vw2 - adj + 1 + (side->vw & 1);
draw_row(side, x1, y, x2 - x1);
}
draw_cursor(side);
flush_output(side);
/* draw_box(side); /* must be after flush */
}
}
/* Draw an individual detailed hex, as a row of one. */
/* This routine may be called in cases where the hex is not on the main */
/* screen; if so, then the world map but not the local map is drawn on. */
/* (should the display be shifted to make visible?) */
draw_hex(side, x, y, flushit)
Side *side;
int x, y;
bool flushit;
{
int sx, sy;
if (active_display(side)) {
if (side->monochrome || side->showmode == TERRICONS) {
xform(side, unwrap(side, x), y, &sx, &sy);
draw_hex_icon(side, side->map, sx, sy, side->bgcolor, HEX);
}
draw_row(side, unwrap(side, x), y, 1);
draw_bar(side, x, y, 1, world_color(side, x, y));
if (flushit) flush_output(side);
}
}
/* The basic map drawing routine does an entire row at a time, which yields */
/* order-of-magnitude speedups (!). This routine is complicated by several */
/* tricks: 1) in monochrome, the entire line can be drawn at once; 2) in */
/* color, run-length encoding maximizes the length of constant-color strings */
/* and 3) anything which is in the background color need not be drawn. */
/* In general, this routine dominates the map viewing process, so efficiency */
/* here is very important. */
draw_row(side, x0, y0, len)
Side *side;
int x0, y0, len;
{
bool empty = TRUE;
char ch;
int i = 0, x, x1, color, segcolor, sx, sy;
if (side->monochrome) {
xform(side, x0, y0, &sx, &sy);
for (x = x0; x < x0 + len; ++x) {
if (side_view(side, wrap(x), y0) == EMPTY) {
rowbuf[i++] = ttypes[terrain_at(wrap(x), y0)].tchar;
empty = FALSE;
} else {
rowbuf[i++] = ' ';
}
}
if (!empty) draw_terrain_row(side, sx, sy, rowbuf, i, side->fgcolor);
} else {
x1 = x0;
segcolor = hex_color(side, x0, y0);
for (x = x0; x < x0 + len; ++x) {
color = hex_color(side, x, y0);
if (color != segcolor) {
if (segcolor != side->bgcolor) {
xform(side, x1, y0, &sx, &sy);
draw_terrain_row(side, sx, sy, rowbuf, i, segcolor);
}
i = 0;
x1 = x;
segcolor = color;
}
switch(side->showmode) {
case FULLHEX:
case BOTHICONS:
ch = HEX;
break;
case BORDERHEX:
ch = OHEX;
break;
case TERRICONS:
ch = ttypes[terrain_at(wrap(x), y0)].tchar;
break;
}
rowbuf[i++] = ch;
}
if (len == 1) i = 1;
xform(side, x1, y0, &sx, &sy);
draw_terrain_row(side, sx, sy, rowbuf, i, segcolor);
if (side->showmode == BOTHICONS) {
i = 0;
x1 = x0;
segcolor = terricon_color(side, x0, y0);
for (x = x0; x < x0 + len; ++x) {
color = terricon_color(side, x, y0);
if (color != segcolor) {
xform(side, x1, y0, &sx, &sy);
draw_terrain_row(side, sx, sy, rowbuf, i, segcolor);
i = 0;
x1 = x;
segcolor = color;
}
rowbuf[i++] = ttypes[terrain_at(wrap(x), y0)].tchar;
}
if (len == 1) i = 1;
xform(side, x1, y0, &sx, &sy);
draw_terrain_row(side, sx, sy, rowbuf, i, segcolor);
}
}
/* Units are much harder to optimize - fortunately they're sparse */
for (x = x0; x < x0 + len; ++x) {
draw_unit(side, x, y0);
}
}
/* Return the color of the hex. (for color displays only) */
hex_color(side, x, y)
Side *side;
int x, y;
{
return ((side_view(side, wrap(x), y) == UNSEEN) ? side->bgcolor :
side->hexcolor[terrain_at(wrap(x), y)]);
}
/* Return the color of a terrain icon overlaying a colored hex. */
terricon_color(side, x, y)
Side *side;
int x, y;
{
return ((side_view(side, wrap(x), y) == UNSEEN) ? side->bgcolor :
(ttypes[terrain_at(wrap(x), y)].dark ? side->fgcolor :
side->bgcolor));
}
/* Draw a single unit icon as appropriate. This *also* has a bunch of */
/* details to worry about: centering of icon in hex, clearing a rectangular */
/* area for the icon, picking a color for the unit, using either a bitmap */
/* or font char, and adding a side number for many-player games. */
/* Must also be careful not to draw black-on-black for units in space. */
/* This routine has also been drafted into drawing populace side numbers */
/* for otherwise empty hexes. */
draw_unit(side, x, y)
Side *side;
int x, y;
{
int view = side_view(side, wrap(x), y), sx, sy, ucolor, hcolor, n;
int terr = terrain_at(wrap(x), y), pop, pcolor;
Side *side2;
if (view != UNSEEN) {
if (view == EMPTY) {
if (populations) {
pop = people_at(wrap(x), y);
if (pop != NOBODY) {
side2 = side_n(pop-8);
pcolor = (allied_side(side, side2) ? side->owncolor :
(enemy_side(side, side2) ? side->enemycolor :
side->neutcolor));
if (pcolor == side->owncolor &&
(ttypes[terr].dark ||
side->monochrome ||
(side->showmode == TERRICONS)))
pcolor = side->fgcolor;
xform(side, x, y, &sx, &sy);
draw_side_number(side, side->map, sx, sy, pop-8, pcolor);
}
}
} else {
xform(side, x, y, &sx, &sy);
side2 = side_n(vside(view));
ucolor = (allied_side(side, side2) ? side->owncolor :
(enemy_side(side, side2) ? side->enemycolor :
side->neutcolor));
if (ucolor == side->owncolor &&
(ttypes[terr].dark ||
side->monochrome ||
(side->showmode == TERRICONS)))
ucolor = side->fgcolor;
if (side->monochrome && side != side2)
ucolor = side->bgcolor;
hcolor = (side == side2 ? side->bgcolor : side->fgcolor);
if (side->monochrome) {
/* erasing background */
draw_hex_icon(side, side->map, sx, sy, hcolor, HEX);
} else if (side->showmode != TERRICONS) {
draw_hex_icon(side, side->map, sx, sy, hex_color(side, x, y),
((side->showmode == BORDERHEX) ? OHEX : HEX));
}
draw_unit_icon(side, side->map, sx, sy, vtype(view), ucolor);
n = side_number(side2);
if ((numsides > 2 || side->monochrome) && n != side_number(side)) {
draw_side_number(side, side->map, sx, sy, n, ucolor);
}
}
}
}
/* Cursor drawing also draws the unit in some other color if it's not the */
/* "top-level" unit in a hex, as well as getting the player's attention */
/* if the new location is sufficiently far from the last. */
draw_cursor(side)
Side *side;
{
int sx, sy;
if (active_display(side)) {
/* ugly hack to prevent extra cursor draw */
if (side->cury == 0) return;
xform(side, unwrap(side, side->curx), side->cury, &sx, &sy);
if (side->curunit != NULL && side->curunit->transport != NULL) {
if (side->monochrome) {
draw_hex_icon(side, side->map, sx, sy, side->bgcolor, HEX);
draw_unit_icon(side, side->map, sx, sy,
side->curunit->type, side->fgcolor);
} else {
draw_unit_icon(side, side->map, sx, sy,
side->curunit->type, side->diffcolor);
}
}
/* Flash something to draw the eye a long ways */
if (humanside(side)) {
if (distance(side->curx, side->cury, side->lastx, side->lasty) > 3)
flash_position(side, sx, sy, DELAY);
draw_cursor_icon(side, sx, sy);
side->lastx = side->curx; side->lasty = side->cury;
}
}
}
/* Get rid of cursor by redrawing the hex. */
erase_cursor(side)
Side *side;
{
if (side->lasty > 0) draw_hex(side, side->lastx, side->lasty, TRUE);
}
int fix_mushroom_hex();
/* Draw a splat visible to both sides at a given location. Several splats */
/* available, depending on the seriousness of the hit. Make an extra-flashy */
/* display when The Bomb goes off. Because of the time delays involved, we */
/* have to update both sides' displays more or less simultaneously. Would be */
/* better to exhibit to all sides maybe, but I'm not going to bother! */
draw_blast(unit, es, hit)
Unit *unit;
Side *es;
int hit;
{
char ch;
int ux = unit->x, uy = unit->y, sx, sy, i;
Side *us = unit->side;
if (hit >= period.nukehit) {
if (active_display(us)) invert_whole_map(us);
if (active_display(es)) invert_whole_map(es);
/* may need a time delay if X is too speedy */
nap(500);
if (active_display(us)) invert_whole_map(us);
if (active_display(es)) invert_whole_map(es);
for (i = 0; i < 4; ++i) {
if (active_display(us)) draw_mushroom(us, ux, uy, i);
if (active_display(es)) draw_mushroom(es, ux, uy, i);
if (i != 2 && (active_display(us) || active_display(es))) sleep(1);
}
if (active_display(us) || active_display(es)) sleep(1);
tmpside = us;
apply_to_area(ux,uy,2,fix_mushroom_hex);
flush_output(us);
tmpside = es;
apply_to_area(ux,uy,2,fix_mushroom_hex);
flush_output(es);
} else {
ch = ((hit >= unit->hp) ? 'd' : ((hit > 0) ? 'c' : 'b'));
if (active_display(us)) {
xform(us, unwrap(us, ux), uy, &sx, &sy);
draw_blast_icon(us, us->map, sx, sy, ch, us->enemycolor);
flush_output(us);
nap(250);
draw_hex(us,ux,uy,TRUE);
}
if (active_display(es)) {
xform(es, unwrap(es, ux), uy, &sx, &sy);
draw_blast_icon(es, es->map, sx, sy, ch, es->owncolor);
flush_output(es);
nap(250);
draw_hex(es, ux, uy,TRUE);
}
}
}
/* Draw all the units in a column. They should be spaced so they don't */
/* overlap or get misaligned with text, and can be inverted if desired. */
draw_unit_list(side, hilite)
Side *side;
bool hilite[];
{
int u, pos = 0, spacing = max(side->hh, side->fh);
if (active_display(side)) {
for_all_unit_types(u) {
draw_hex_icon(side, side->state, side->margin, pos,
(hilite[u] ? side->fgcolor : side->bgcolor), OHEX);
draw_unit_icon(side, side->state, side->margin, pos, u,
(hilite[u] ? side->bgcolor : side->fgcolor));
pos += spacing;
}
}
}
fix_mushroom_hex(x,y)
int x,y;
{
draw_hex(tmpside,x,y,FALSE);
}